Docker와 Jenkins를 이용한 CI-CD 파이프라인 구축 가이드(Ubuntu)

CI/CD 간단 구성 가이드

1. 기본 환경 설정 (Ubuntu 기준 Docker & Jenkins 설치)

# Docker 설치
sudo apt update
sudo apt install -y docker.io
sudo systemctl start docker
sudo systemctl enable docker

# sudo를 붙이지 않고 docker 명령어 사용하기 위함
sudo usermod -aG docker [username]

# Jenkins 컨테이너 실행
# --name jenkins          : 컨테이너 이름 지정
# --privileged            : 컨테이너에 추가 권한 부여, 편의상 privileged 사용
#                           (Docker outside of Docker에서는 불필요한 설정 <-> Docker in Docker)
# -p 8080:8080            : Jenkins 웹 인터페이스용 포트 매핑
# -p 50000:50000          : Jenkins 에이전트 연결용 포트 매핑
# -v jenkins_home:...     : Jenkins 데이터 영구 저장을 위한 볼륨 마운트
# -v /var/run/docker.sock : Docker 소켓 파일 마운트(Docker outside of Docker를 위한 소켓)
# -v $(which docker):...  : Docker 실행 파일 마운트
# jenkins/jenkins:lts     : Jenkins LTS 버전 이미지 사용
sudo docker run -d \
  --name jenkins \
  --privileged \
  -p 8080:8080 \
  -p 50000:50000 \
  -v jenkins_home:/var/jenkins_home \
  -v /var/run/docker.sock:/var/run/docker.sock \
  -v $(which docker):/usr/bin/docker \
  jenkins/jenkins:lts

# Jenkins 컨테이너 내부에서 docker 그룹 권한 설정
sudo docker exec -u 0 jenkins bash -c "\
  groupadd -g $(getent group docker | cut -d: -f3) docker && \
  usermod -aG docker jenkins"

sudo docker restart jenkins

# Docker 소켓 권한 변경
sudo chmod 666 /var/run/docker.sock

# (프로덕션 환경에서는 권한을 제한적으로 사용 : 
# ex - 그룹 소유권을 docker로 변경 
# sudo chown root:docker /var/run/docker.sock
# sudo chmod 660 /var/run/docker.sock)
2026-10 갱신

  • 기존에는 \로 이어지는 docker run 명령 줄 사이에 # 주석 줄이 끼어 있어, 복사·붙여넣기 시 명령이 중간에 끊기고 나머지 옵션이 별도 명령으로 실행되는 문제가 있었습니다. 주석을 명령 위로 옮겼습니다.
  • apt install docker.io는 Ubuntu 배포판 패키지라 버전이 다소 오래될 수 있습니다. 최신 버전이 필요하면 Docker 공식 apt 저장소를 등록한 뒤 docker-ce, docker-ce-cli, containerd.io 등을 설치합니다(docs.docker.com의 Ubuntu 설치 문서 참고).
  • 호스트의 docker 바이너리를 마운트(-v $(which docker):/usr/bin/docker)하는 방식은 호스트와 컨테이너의 glibc 등 라이브러리 버전이 다르면 실행에 실패할 수 있습니다. 컨테이너 이미지 안에 Docker CLI를 직접 설치하는 편이 안정적입니다.
FROM jenkins/jenkins:lts
USER root
RUN apt-get update && apt-get install -y ca-certificates curl \
 && install -m 0755 -d /etc/apt/keyrings \
 && curl -fsSL https://download.docker.com/linux/debian/gpg -o /etc/apt/keyrings/docker.asc \
 && echo "deb [signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/debian $(. /etc/os-release && echo $VERSION_CODENAME) stable" > /etc/apt/sources.list.d/docker.list \
 && apt-get update && apt-get install -y docker-ce-cli \
 && rm -rf /var/lib/apt/lists/*
USER jenkins

(jenkins/jenkins:lts는 Debian 기반이라 Docker 저장소도 debian 경로를 사용합니다.)

2026-10 갱신 - 보안 주의

sudo chmod 666 /var/run/docker.sock은 호스트의 모든 사용자/프로세스에 Docker 데몬(사실상 root) 권한을 열어 주므로 권장하지 않습니다. 위에서 이미 호스트 docker 그룹과 같은 GID로 그룹을 만들어 jenkins 사용자를 추가했으므로, 소켓은 root:docker + 660을 유지하는 방식(그룹 기반 접근)으로 충분합니다. 또한 소켓 마운트 자체가 컨테이너에 호스트 root에 준하는 권한을 주는 것이므로, DooD 방식에서는 --privileged도 빼는 것이 좋습니다.

추가 설명

Docker 소켓 및 실행 파일 마운트 부분

  1. /var/run/docker.sock: Docker 데몬과 통신하기 위한 소켓 파일. 이를 Jenkins 컨테이너에 마운트함으로써, Jenkins 컨테이너 내부에서 호스트의 Docker 데몬을 사용할 수 있게 됨.

  2. $(which docker):/usr/bin/docker: 호스트의 Docker 실행 파일을 Jenkins 컨테이너 내부의 /usr/bin/docker 위치에 마운트 시킴. 이를 통해 Jenkins 컨테이너 내부에서 Docker 명령어를 실행할 수 있게 됨. $(which docker)를 사용하면 docker 실행 파일의 정확한 경로를 자동으로 찾아줌

Docker 그룹 권한 설정 부분

  1. docker exec -u 0: root 권한으로 Jenkins 컨테이너 내부에서 명령을 실행. (-u 0은 root 사용자를 의미)
  2. groupadd -g $(getent group docker | cut -d: -f3) docker: 호스트의 docker 그룹과 동일한 GID(Group ID)를 가진 docker 그룹을 컨테이너 내부에 생성.
  3. usermod -aG docker jenkins: jenkins 사용자를 docker 그룹에 추가. 이를 통해 jenkins 사용자가 Docker 명령어를 실행할 수 있는 권한을 얻게 됨.

※ Docker 권한 및 Docker in Docker (DinD) 심화 설정

2. 프로젝트 구조 설정(깃허브 작업)

main branch의 nginx-test/ (예시) 아래에 작성

Pasted image 20250418130906.png

Dockerfile

# nginx 알파인 리눅스 버전을 기본 이미지로 사용
FROM nginx:alpine

# 로컬의 html 디렉토리를 nginx의 기본 웹 루트 디렉토리로 복사
COPY html /usr/share/nginx/html

# 컨테이너의 80번 포트를 외부에 노출
EXPOSE 80

html/index.html (nginx-test/html/ 아래에 작성)

<!DOCTYPE html>
<html>
<head>
    <!-- UTF-8 인코딩 설정으로 한글 깨짐 방지 -->
    <meta charset="UTF-8">
    <title>Nginx Test</title>
</head>
<body>
    <!-- 테스트용 웹페이지 내용 -->
    <h1>Hello from Nginx</h1>
    <p>테스트 개발 빌드입니다</p>
    <p>This is a test page deployed via Jenkins pipeline.</p>
</body>
</html>

Jenkinsfile (main branch의 nginx-test/ 아래에 작성)

// 파이프라인 정의 시작
pipeline {
    // 모든 에이전트에서 실행 가능하도록 설정
    agent any
    
    // 환경 변수 설정
    environment {
        // Docker 이미지 이름 설정
        DOCKER_IMAGE = "my-nginx"
        // 빌드 번호를 태그로 사용
        DOCKER_TAG = "${BUILD_NUMBER}"
    }
    
    // 파이프라인 단계 정의
    stages {
        // Git 저장소에서 코드 체크아웃
        stage('Checkout') {
            steps {
                git branch: 'main',
                    url: 'https://github.com/[username]/[repository].git'
            }
        }
        
        // Docker 이미지 빌드
        stage('Build') {
            steps {
                // nginx-test 디렉토리로 이동
                dir('nginx-test') {
                    // Docker 이미지 빌드 명령 실행
                    sh 'docker build -t $DOCKER_IMAGE:$DOCKER_TAG .'
                }
            }
        }
        
        // 새 버전 배포
        stage('Deploy') {
            steps {
                sh '''
                    # 기존 컨테이너 중지 (실패해도 계속 진행)
                    docker stop nginx-app || true
                    # 기존 컨테이너 제거
                    docker rm nginx-app || true
                    # 새 버전 컨테이너 실행
                    docker run -d --name nginx-app -p 8090:80 $DOCKER_IMAGE:$DOCKER_TAG
                '''
            }
        }
    }
    
    // 파이프라인 완료 후 실행될 작업
    post {
        // 성공 시 메시지 출력
        success {
            echo 'Pipeline succeeded! Nginx is deployed.'
        }
        // 실패 시 메시지 출력
        failure {
            echo 'Pipeline failed! Check the logs for details.'
        }
    }
}

3. Jenkins 설정

4. GitHub Webhook 설정

GitHub 저장소 Settings > Webhooks > Add webhook

5. 접속 및 테스트

작동 원리

  1. GitHub에 코드 변경사항 push
  2. GitHub webhook이 Jenkins에 알림
  3. Jenkins가 코드를 가져와서 Docker 이미지 빌드
  4. 기존 컨테이너 중지 및 제거
  5. 새로운 버전의 컨테이너 배포

관련 문서